package org.dreamwork.assistant.tools.persistence.gui;

import com.intellij.uiDesigner.core.GridConstraints;
import com.intellij.uiDesigner.core.GridLayoutManager;
import com.intellij.uiDesigner.core.Spacer;
import org.dreamwork.compilation.*;
import org.dreamwork.ui.concurrent.Handler;
import org.dreamwork.ui.concurrent.IMessageHandler;
import org.dreamwork.ui.concurrent.Message;
import org.dreamwork.util.FileInfo;
import org.dreamwork.util.StringUtil;

import javax.swing.*;
import javax.swing.filechooser.FileFilter;
import javax.swing.filechooser.FileSystemView;
import javax.swing.text.BadLocationException;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.*;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.*;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Created by seth.yang on 2017/6/22
 */
public class MainFrame extends JFrame implements IMessageHandler, ActionListener {
    private JPanel root;
    private JButton btnQuit;
    private JButton btnGenerate;
    private JComboBox<String> cmbFile;
    private JButton btnScriptFile;
    private JComboBox<String> cbDatabase;
    private JTextPane txtLogger;
    private JComboBox<String> cbPackageName;
    private JComboBox<String> cbOutPath;
    private JButton btnOutPath;
    private JButton btnClear;
    private JCheckBox cbPrimitive;
    private JCheckBox cbEquals;
    private JTextField txtServer;
    private JTextField txtUserName;
    private JPasswordField txtPassword;
    private JComboBox<String> cbDatabaseName;
    private JLabel lblDBServer;
    private JLabel lblDBName;
    private JLabel lblUserName;
    private JLabel lblPassword;
    private JTextField txtPort;
    private JLabel lblPort;
    private JCheckBox chkJson;
    private JCheckBox chkDirtyMark;

    private JComponent[] groups;

    private Handler handler;
    private JFileChooser chooser;
    private File conf;
    private static final String CRLF = String.format ("%n");
    private final Object LOCKER = new byte[0];
    private FileFilter SQL_FILTER = new FileFilter () {
        @Override
        public boolean accept (File f) {
            if (f.isDirectory ()) {
                return true;
            }

            String name = f.getName ();
            return name.toLowerCase ().endsWith (".sql");
        }

        @Override
        public String getDescription () {
            return "SQL Script Files";
        }
    };
    private FileFilter DIR_FILTER = new FileFilter () {
        @Override
        public boolean accept (File f) {
            return f.isDirectory ();
        }

        @Override
        public String getDescription () {
            return "Directories";
        }
    };

    private static final String
            SCRIPT_NAME = "org.dreamwork.tools.SCRIPT_NAME",
            OUT_DIR_NAME = "org.dreamwork.tools.OUT_DIR_NAME",
            DATABASE_NAME = "org.dreamwork.tools.DATABASE_NAME",
            PACKAGE_NAME = "org.dreamwork.tools.PACKAGE_NAME",
            PRIMITIVE = "org.dreamwork.tools.PRIMITIVE",
            EQUALS = "org.dreamwork.tools.EQUALS";

    private static final String PG_SQL = "PostgreSQL";

    private static final int
            FLUSH_ATTRIBUTES = 0,
            READ_ATTRIBUTES = 1,
            FILL_UI_DATA = 2,
            ALARM = 3,
            GENERATE = 4,
            UNLOCK_UI = 5;

    private Map<String, Object> attributes = new HashMap<> ();
    private Set<String> schemas = new HashSet<> ();
    private File work;

    public static void main (String[] args) throws Exception {
        UIManager.setLookAndFeel (UIManager.getSystemLookAndFeelClassName ());
        new MainFrame ().setVisible (true);
    }

    public MainFrame () throws IOException {
        super ("SQL Generator");

        handler = new Handler (this);
        init ();
    }

    private void init () throws IOException {
        setContentPane (root);

        URL url = getClass ().getClassLoader ().getResource ("source_java.png");
        if (url != null) {
            ImageIcon icon = new ImageIcon (url);
            setIconImage (icon.getImage ());
        }

        Toolkit toolkit = Toolkit.getDefaultToolkit ();
        Dimension D = toolkit.getScreenSize ();
        int width = D.width / 2, height = D.height / 2;
        setSize (width, height);
        setLocation ((D.width - width) / 2, (D.height - height) / 2);

        SwingTool.addConfirmExit (this);

        // gui setup
        btnGenerate.addActionListener (this);
        btnOutPath.addActionListener (this);
        btnQuit.addActionListener (this);
        btnScriptFile.addActionListener (this);
        cmbFile.addActionListener (this);
        btnClear.addActionListener (this);
        cbPrimitive.addActionListener (this);

        Path confDir = Paths.get ("../work");
        if (!Files.exists (confDir)) {
            Files.createDirectories (confDir);
        }
        work = confDir.toFile ();

        Path config = Paths.get (".conf");
        if (!Files.exists (config)) {
            Files.createFile (config);
        }
        conf = config.toFile ();

        chooser = new JFileChooser (".");
        chooser.setMultiSelectionEnabled (false);

        cmbFile.setName (SCRIPT_NAME);
        cbOutPath.setName (OUT_DIR_NAME);
        cbPackageName.setName (PACKAGE_NAME);

        cbDatabase.addItem ("SQLite");
        cbDatabase.addItem ("Derby");
        cbDatabase.addItem (PG_SQL);

        doc = txtLogger.getStyledDocument ();
        normal = doc.addStyle ("normal", null);
        StyleConstants.setForeground (normal, Color.black);
        StyleConstants.setFontFamily (normal, "Consolas");

        error = doc.addStyle ("error", normal);
        StyleConstants.setForeground (error, Color.red);

        success = doc.addStyle ("success", error);
        StyleConstants.setForeground (success, Color.green.darker ());

        warning = doc.addStyle ("warning", normal);
        StyleConstants.setForeground (warning, Color.orange);

        groups = new JComponent[] {
                lblDBName, lblDBServer, lblPassword, lblUserName, lblPort,
                cbDatabaseName, txtPassword, txtServer, txtUserName, txtPort
        };
        txtPort.setText ("5432");
        txtPort.setDocument (new IntegerOnlyDocument ());

        //load the history data
        handler.sendNonUIMessage (READ_ATTRIBUTES);

        cbDatabase.addActionListener (this);
    }

    @Override
    public void handleUIMessage (Message message) {
        switch (message.what) {
            case FILL_UI_DATA:
                fillUiData ();
                break;
            case ALARM:
                JOptionPane.showMessageDialog (this, message.obj, "Error", JOptionPane.ERROR_MESSAGE);
                break;
            case UNLOCK_UI:
                setUI (true);
                break;
        }
    }

    @Override
    public void handleNonUIMessage (Message message) {
        try {
            switch (message.what) {
                case FLUSH_ATTRIBUTES:
                    flushAttributes ();
                    break;
                case READ_ATTRIBUTES:
                    loadAttributes ();
                    break;
                case GENERATE:
                    @SuppressWarnings ("unchecked")
                    Map<String, String> map = (Map<String, String>) message.obj;
                    String script = map.get (SCRIPT_NAME);
                    String name = map.get (PACKAGE_NAME);
                    String out = map.get (OUT_DIR_NAME);
                    String db = map.get (DATABASE_NAME);
                    boolean primitive = Boolean.parseBoolean (map.get (PRIMITIVE));
                    boolean equals = Boolean.parseBoolean (map.get (EQUALS));
                    generate (script, name, out, db, primitive, equals);
                    break;
            }
        } catch (Exception ex) {
            error (ex);
        }
    }

    @Override
    public void actionPerformed (ActionEvent e) {
        try {
            Object button = e.getSource ();
            if (button == btnGenerate) {
                generate ();
            } else if (button == btnOutPath) {
                selectPath (cbOutPath, DIR_FILTER, true);
            } else if (button == btnQuit) {
                SwingTool.confirmExit (this);
            } else if (button == btnScriptFile) {
                selectPath (cmbFile, SQL_FILTER, false);
            } else if (button == btnClear) {
                txtLogger.setText ("");
            } else if (button == cbDatabase) {
                String dbType = (String) cbDatabase.getSelectedItem ();
                boolean state = PG_SQL.equalsIgnoreCase (dbType);
                for (JComponent c : groups) {
                    c.setEnabled (state);
                }
            } else {
                String actionName = e.getActionCommand ();
                if ("comboBoxEdited".equals (actionName)) {
                    if (button == cmbFile) {
                        setValue (cmbFile, (String) cmbFile.getSelectedItem ());
                    }
                }
            }
        } catch (Exception ex) {
            error (ex);
        }
    }

    @SuppressWarnings ("unchecked")
    private void loadAttributes () throws Exception {
        synchronized (LOCKER) {
            try (InputStream in = new FileInputStream (conf)) {
                ObjectInputStream ois = new ObjectInputStream (in);
                attributes = (Map<String, Object>) ois.readObject ();
            } catch (EOFException ex) {
                warn ("can't read config file");
            }

            handler.sendUIMessage (FILL_UI_DATA);
        }
    }

    private void flushAttributes () throws IOException {
        synchronized (LOCKER) {
            try (OutputStream out = new FileOutputStream (conf)) {
                ObjectOutputStream oos = new ObjectOutputStream (out);
                oos.writeObject (attributes);
                out.flush ();
            }
        }
    }

    private void generate () {
        String script = (String) cmbFile.getSelectedItem ();
        if (StringUtil.isEmpty (script)) {
            alarm ("The script file is missing");
            return;
        }

        setUI (false);

        Map<String, String> map = new HashMap<> ();
        map.put (SCRIPT_NAME, script);
        String dir = (String) cbPackageName.getSelectedItem ();
        if (!StringUtil.isEmpty (dir)) {
            map.put (PACKAGE_NAME, dir);
        }
        String out = (String) cbOutPath.getSelectedItem ();
        if (!StringUtil.isEmpty (out)) {
            map.put (OUT_DIR_NAME, out);
        }
        String db = (String) cbDatabase.getSelectedItem ();
        map.put (DATABASE_NAME, db);

        map.put (PRIMITIVE, String.valueOf (cbPrimitive.isSelected ()));
        map.put (EQUALS, String.valueOf (cbEquals.isSelected ()));

        handler.sendNonUIMessage (GENERATE, map);
    }

    private void generate (String script, String name, String out, String db, boolean primitive, boolean genEquals) throws IOException, SQLException {
        try {
            File file = new File (script);
            if (!file.exists ()) {
                alarm ("file " + script + " not found!");
                return;
            }

            if (StringUtil.isEmpty (name)) {
                warn ("package name is not provided, using the default packet name");
                name = "";
            }

            if (StringUtil.isEmpty (out)) {
                out = "../out";
            }

            String dirName = name.replace ('.', '/');
            File base = new File (out, dirName);
            if (!base.exists () && !base.mkdirs ()) {
                error ("Can't create directory: " + base.getCanonicalPath ());
                return;
            }

            info (base.getCanonicalPath () + " created.");
            info ("parsing the script file...");

            try (Connection conn = createDB (db)) {
                if (conn == null) {
                    error ("can't init temp database");
                    return;
                }

                Map<String, String> map = readAndCreateTables (conn, file);
                if (PG_SQL.equals (cbDatabase.getSelectedItem ())) {
                    generatePostgreSQL (conn, map, name, base, primitive, genEquals);
                } else {
                    generateEmbeddedDatabase (conn, map, name, base, primitive, genEquals);
                }

                generateSchemaClass (base, file);

                success ("All job done !");
            }
        } finally {
            handler.sendUIMessage (UNLOCK_UI);
        }
    }

    private void generatePostgreSQL (Connection conn, Map<String, String> map, String name, File base, boolean primitive, boolean genEquals) throws SQLException, IOException {
        DatabaseMetaData dmd = conn.getMetaData ();
        for (String tableName : map.keySet ()) {
            String sql = map.get (tableName);
            tableName = tableName.toLowerCase ();
            ResultSet rs = dmd.getTables (null, null, tableName, new String[] {"TABLE"});
            if (!rs.next ()) {
                Statement stmt = conn.createStatement ();
                stmt.execute (map.get (tableName));

                rs = dmd.getTables (null, null, tableName, new String[] {"TABLE"});
                if (!rs.next ()) {
                    warn ("can't generate table: " + tableName);
                    continue;
                }
            }

            info ("generating table " + tableName + "...");
            generate (dmd, tableName, name, base, sql, primitive, genEquals);
        }
    }

    private void generateEmbeddedDatabase (Connection conn, Map<String, String> map, String name, File base, boolean primitive, boolean genEquals) throws SQLException, IOException {
        DatabaseMetaData dmd = conn.getMetaData ();

        ResultSet rs = dmd.getTables (null, null, "%", new String[] {"TABLE"});
        while (rs.next ()) {
            String tableName = rs.getString ("TABLE_NAME");
            String sql = map.get (tableName.toUpperCase ());
            if (sql != null) {
                info ("generating table " + tableName + "...");
                generate (dmd, tableName, name, base, sql, primitive, genEquals);
            }
        }
    }

    private void generateSchemaClass (File base, File file) throws IOException {
        String schemaName = toJavaName (FileInfo.getFileNameWithoutExtension (file.getName ()), true);
        String packageName = (String) cbPackageName.getSelectedItem ();
        VirtualClass vc = new VirtualClass ()
                .setName (schemaName)
                .addImport ("org.dreamwork.persistence.DatabaseSchema");
        if (!StringUtil.isEmpty (packageName)) {
            vc.setPackageName (packageName.trim ());
        }
        VirtualMethod vm = new VirtualMethod ().setName ("registerAllSchemas").setStill (true);
        for (String schema : schemas) {
            vm.addStatement (
                    "DatabaseSchema.register(%s.class)",
                    schema
            );
        }
        vc.addMethod (vm);
        File entry = new File (base, schemaName + ".java");
        try (OutputStream o = new FileOutputStream (entry)) {
            PrintWriter pw = new PrintWriter (o, true);
            pw.println (vc);
            pw.flush ();
            o.flush ();
        }
    }

    private Map<String, String> readAndCreateTables (Connection conn, File script) throws SQLException, IOException {
        Statement stmt = conn.createStatement ();
        StringBuilder builder = new StringBuilder ();
        Pattern P = Pattern.compile ("create\\s+table\\s+(.*?)\\s+", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
        Map<String, String> map = new IgnoreCaseHashMap<> ();

        boolean pg = PG_SQL.equals (cbDatabase.getSelectedItem ());

        try (FileReader reader = new FileReader (script)) {
            int ch;
            while ((ch = reader.read ()) != -1) {
                switch (ch) {
                    case ';':
                        String sql = builder.toString ().trim ();
                        Matcher m = P.matcher (sql);
                        if (m.find ()) {
                            String tableName = m.group (1);
                            if (!pg)
                                stmt.execute (sql);
                            map.put (tableName.toUpperCase (), sql);
                            info ("table: " + tableName + " parsed");
                        }
                        builder.setLength (0);
                        break;
                    case '-':
                        if (builder.charAt (builder.length () - 1) == '-') {
                            // comment
                            builder.deleteCharAt (builder.length () - 1);
                            while ((ch = reader.read ()) != -1) {
                                if (ch == '\n') {
                                    builder.append ((char) ch);
                                    break;
                                }
                            }
                        } else {
                            builder.append ((char) ch);
                        }
                        break;
                    case '*':
                        if (builder.charAt (builder.length () - 1) == '/') {
                            // block comment
                            builder.deleteCharAt (builder.length () - 1);
                            int last = -1;
                            while ((ch = reader.read ()) != -1) {
                                if (last == '*' && ch == '/') {
                                    break;
                                }
                                last = ch;
                            }
                        }
                    default:
                        builder.append ((char) ch);
                }
            }
        }

        return map;
    }

    private String toJavaName (String name, boolean isClass) {
        StringBuilder builder = new StringBuilder ();
        while (name.charAt (0) == '_')
            name = name.substring (1);
        boolean upperCase = false;
        for (char ch : name.toCharArray ()) {
            if ((builder.length () == 0 && isClass) || upperCase) {
                builder.append (Character.toUpperCase (ch));
                upperCase = false;
            } else if (ch == '_') {
                upperCase = true;
            } else {
                builder.append (Character.toLowerCase (ch));
            }
        }

        return builder.toString ();
    }

    private void generate (DatabaseMetaData dmd, String tableName, String packageName, File base, String sql, boolean primitive, boolean genEquals) throws SQLException, IOException {
        File dir = new File (base, "schema");
        if (!dir.exists () && !dir.mkdirs ()) {
            throw new IOException ("Can't create dir: schema!");
        }

        String schemaPackageName = StringUtil.isEmpty (packageName) ? "schema" : packageName + ".schema";
        String schemaName = toJavaName (tableName, true) + "Schema";
        String beanName = toJavaName (tableName, true);

        VirtualClass schema = new VirtualClass ()
                .addImport ("org.dreamwork.persistence.*")
                .setSuperClassName ("DatabaseSchema")
                .setName (schemaName);
        VirtualConstruction c = schema.setPackageName (schemaPackageName).addDefaultConstruction ();
        StringBuilder builder = new StringBuilder ("tableName = \"").append (tableName).append ("\"");
        c.addStatement (builder);

        VirtualClass bean = new VirtualClass ()
                .addImport ("org.dreamwork.persistence.*")
                .addAnnotation (new VirtualAnnotation ("ISchema").setValue (schema))
                .setName (beanName);
        if (!StringUtil.isEmpty (packageName)) {
            bean.setPackageName (packageName);
        }

        ResultSet rs = dmd.getPrimaryKeys (null, null, tableName);
        Set<String> set = new HashSet<> ();
        while (rs.next ()) {
            set.add (rs.getString ("COLUMN_NAME").toUpperCase ());
        }
        rs.close ();
        rs = dmd.getColumns (null, null, tableName, "%");

        StringBuilder schemaFields = new StringBuilder ("fields = new String[] {");
        int index = 0;
        String pkName = null;
        VirtualMethod equals = null, hashCode = null;
        if (genEquals) {
            equals = new VirtualMethod ().setName ("equals")
                    .addAnnotation (new VirtualAnnotation ("Override"))
                    .setModifier (Modifier.PUBLIC).setReturnType ("boolean")
                    .addParameter (
                            new VirtualParameter ().setType ("java.lang.Object").setName ("o")
                    ).addStatement ("if (this == o) return true")
                    .addStatement ("if (o == null || getClass () != o.getClass ()) return false")
                    .addStatement (new StringBuilder (beanName).append (" that = (").append (beanName).append (") o"));
            hashCode = new VirtualMethod ().setName ("hashCode")
                    .addAnnotation (new VirtualAnnotation ("Override"))
                    .setModifier (Modifier.PUBLIC).setReturnType ("int");
        }

        while (rs.next ()) {
            boolean isPk = false;
            String name = rs.getString ("COLUMN_NAME"), auto = "";
            int type = rs.getInt ("DATA_TYPE");

            try {
                auto = rs.getString ("IS_AUTOINCREMENT");
            } catch (Exception ex) {
                warn (ex.getMessage ());
            }

            if (index != 0) {
                schemaFields.append (", ");
            }
            schemaFields.append ("\"").append (name).append ("\"");
            if (set.contains (name.toUpperCase ())) {
                pkName = name;
                isPk = true;
            }

            index++;

            VirtualAnnotation va = new VirtualAnnotation ("ISchemaField")
                    .addParameter ("name", name);
            String fieldName = toJavaName (name, false);
            if (isPk) {
                va.addParameter ("id", true);
                if (equals != null)
                    equals.addStatement (
                            primitive ?
                                    new StringBuilder ("return ").append (fieldName).append (" == that.").append (fieldName) :
                                    new StringBuilder ("return ").append (fieldName).append (" != null && ")
                                            .append (fieldName).append (".equals (that.").append (fieldName).append (")")
                    );
                if (hashCode != null)
                    hashCode.addStatement (
                            primitive ?
                                    new StringBuilder ("return String.valueOf (").append (fieldName).append (").hashCode ()") :
                                    new StringBuilder ("return ").append (fieldName).append (" != null ? ")
                                            .append (fieldName).append (".hashCode () : 0")
                    );
            }
            if ("YES".equals (auto)) {
                va.addParameter ("autoincrement", true);
            }

            VirtualField vf = new VirtualField ()
                    .setType (getTypeName (type, primitive))
                    .addAnnotation (va)
                    .setModifier (Modifier.PRIVATE)
                    .setName (fieldName);
            if (chkJson.isSelected ()) {
                vf.addAnnotation (new VirtualAnnotation ("com.google.gson.annotations.Expose"))
                        .addAnnotation (
                                new VirtualAnnotation ("com.google.gson.annotations.SerializedName")
                                        .setValue (name)
                        );
            }

            bean.addField (vf, true);
        }
        if (genEquals)
            bean.addMethod (equals).addMethod (hashCode);

        c.addStatement (schemaFields.append ("};"));
        schema.addMethod (new VirtualMethod ()
                .addAnnotation (new VirtualAnnotation ("Override"))
                .setReturnType ("String")
                .setName ("getPrimaryKeyName")
                .addStatement (new StringBuilder ().append ("return \"").append (pkName).append ("\""))
        ).addMethod (new VirtualMethod ()
                .addAnnotation (new VirtualAnnotation ("Override"))
                .setReturnType ("String")
                .setName ("getCreateDDL")
                .addStatement (new StringBuilder ()
                        .append ("\t\treturn \"")
                        .append (sql.replace ("\r", "")
                                .replace ("\n", "\" +" + CRLF + "\""))
                        .append ("\";"))
        );

        File schemaFile = new File (base, "schema/" + schemaName + ".java");
        log (LVL_INFO, "    " + schemaFile.getCanonicalPath (), normal);
        File beanFile = new File (base, beanName + ".java");
        FileWriter w = new FileWriter (schemaFile);
        w.write (schema.toString ());
        w.flush ();
        w.close ();
        success (" generated.");

        log (LVL_INFO, "    " + beanFile.getCanonicalPath (), normal);
        w = new FileWriter (beanFile);
        w.write (bean.toString ());
        w.flush ();
        w.close ();
        success (" generated.");

        schemas.add (schema.getReferenceName ());
    }

    private String getTypeName (int type, boolean primitive) {
        String typeName;
        switch (type) {
            case Types.INTEGER:
            case Types.SMALLINT:
            case Types.TINYINT:
            case Types.BIT:
                typeName = primitive ? "int" : "Integer";
                break;
            case Types.BIGINT:
                typeName = primitive ? "long" : "Long";
                break;
            case Types.DOUBLE:
                typeName = primitive ? "double" : "Double";
                break;
            case Types.FLOAT:
                typeName = primitive ? "float" : "Float";
                break;
            case Types.NUMERIC:
                typeName = "java.math.BigDecimal";
                break;
            case Types.TIMESTAMP:
                typeName = "java.sql.Timestamp";
                break;
            case Types.DATE:
                typeName = "java.sql.Date";
                break;
            default:
                typeName = "String";
                break;
        }
        return typeName;
    }

    private Connection createDB (String db) {
        String name = String.valueOf (Math.random ()).substring (2);
        switch (db) {
            case "SQLite":
                File dbFile = new File (work, "sqlite-" + name + ".db");
                return DBUtil.getSQLite (dbFile);
            case "Derby":
                File dir = new File (work, "derby-" + name);
                return DBUtil.getDerby (dir);
            case PG_SQL:
                String s_port = txtPort.getText ();
                int port = 5432;
                try {
                    port = Integer.parseInt (s_port);
                } catch (Exception ex) {
                    // ignore
                }

                String server = txtServer.getText ();
                String userName = txtUserName.getText ();
                String password = new String (txtPassword.getPassword ());
                String dbName = (String) cbDatabaseName.getSelectedItem ();
                String url = String.format ("jdbc:postgresql://%s:%d/%s", server, port, dbName);
                return DBUtil.getPostgreSQL (url, userName, password);
        }

        return null;
    }

    private void setUI (boolean state) {
        cmbFile.setEnabled (state);
        cbPackageName.setEnabled (state);
        cbOutPath.setEnabled (state);
        cbDatabase.setEnabled (state);

        btnScriptFile.setEnabled (state);
        btnOutPath.setEnabled (state);
        btnGenerate.setEnabled (state);
    }

    private void selectPath (JComboBox<String> box, FileFilter filter, boolean dir) throws IOException {
        String path = (String) box.getSelectedItem ();
        if (StringUtil.isEmpty (path)) {
            path = "";
        } else {
            File f = new File (path);
            path = f.getParentFile ().getCanonicalPath ();
        }
        chooser.setCurrentDirectory (new File (path));
        chooser.setFileFilter (filter);
        chooser.setSelectedFile (null);
        chooser.setFileSystemView (FileSystemView.getFileSystemView ());
        chooser.setSelectedFiles (null);
        if (dir) {
            chooser.setFileSelectionMode (JFileChooser.DIRECTORIES_ONLY);
        } else {
            chooser.setFileSelectionMode (JFileChooser.FILES_ONLY);
        }

        chooser.showOpenDialog (this);
        File file = chooser.getSelectedFile ();
        if (file != null) {
            path = file.getCanonicalPath ();
            setValue (box, path);
        }
    }

    private static final int LVL_INFO = 0, LVL_WARN = 1, LVL_ERROR = 2, LVL_SUCCESS = 3;

    private void warn (String message) {
        log (LVL_WARN, message, warning);
        log (LVL_WARN, CRLF, warning);
    }

    private void info (String message) {
        log (LVL_INFO, message, normal);
        log (LVL_INFO, CRLF, normal);
    }

    private void error (String message) {
        log (LVL_ERROR, message, error);
        log (LVL_ERROR, CRLF, error);
    }

    private void success (String message) {
        log (LVL_SUCCESS, message, success);
        log (LVL_SUCCESS, CRLF, success);
    }

    private void error (Exception ex) {
        error (ex.getMessage ());
        StringWriter sw = new StringWriter ();
        PrintWriter pw = new PrintWriter (sw);
        ex.printStackTrace (pw);
        error (sw.getBuffer ().toString ());
    }

    private void log (int level, String text, Style style) {
        System.out.printf ("[%d] %s%n", level, text);
        parameters.clear ();
        parameters.put ("text", text);
        parameters.put ("style", style);
        handler.sendUIMessage (APPEND_LOGGER, parameters);
        synchronized (LOCKER) {
            try {
                doc.insertString (doc.getLength (), text, style);
                txtLogger.moveCaretPosition (doc.getLength ());
            } catch (BadLocationException e) {
                e.printStackTrace ();
            }
        }
    }

    @SuppressWarnings ("unchecked")
    private void fillUiData () {
        Set<String> set = (Set<String>) attributes.get (SCRIPT_NAME);
        if (set != null && !set.isEmpty ()) {
            appendValues (cmbFile, set);
        }

        set = (Set<String>) attributes.get (PACKAGE_NAME);
        if (set != null && !set.isEmpty ()) {
            appendValues (cbPackageName, set);
        }

        set = (Set<String>) attributes.get (OUT_DIR_NAME);
        if (set != null && !set.isEmpty ()) {
            appendValues (cbOutPath, set);
        }
    }

    @SuppressWarnings ("unchecked")
    private void setValue (JComboBox<String> box, String value) {
        if (value == null || box == null) {
            return;
        }
        ComboBoxModel<String> model = box.getModel ();
        int size = model.getSize ();
        for (int i = 0; i < size; i++) {
            if (value.equals (model.getElementAt (i))) {
                model.setSelectedItem (value);
                return;
            }
        }

        box.addItem (value);
        box.setSelectedItem (value);
        String name = box.getName ();
        Set<String> values = (Set<String>) attributes.get (name);
        if (values == null) {
            values = new TreeSet<> ();
            for (int i = 0; i < size; i++) {
                values.add (model.getElementAt (i));
            }
        } else {
            values.add (value);
        }
        attributes.put (name, values);
        handler.sendNonUIMessage (FLUSH_ATTRIBUTES);
    }

    private void alarm (String message) {
        handler.sendUIMessage (ALARM, message);
    }

    private void appendValues (JComboBox<String> box, Set<String> values) {
        box.addItem ("");
        for (String value : values) {
            box.addItem (value);
        }
    }

    private Map<String, Object> parameters = new HashMap<> ();
    private static final int APPEND_LOGGER = 99;
    private StyledDocument doc;
    private Style normal, error, success, warning;

    {
// GUI initializer generated by IntelliJ IDEA GUI Designer
// >>> IMPORTANT!! <<<
// DO NOT EDIT OR ADD ANY CODE HERE!
        $$$setupUI$$$ ();
    }

    /**
     * Method generated by IntelliJ IDEA GUI Designer
     * >>> IMPORTANT!! <<<
     * DO NOT edit this method OR call it in your code!
     *
     * @noinspection ALL
     */
    private void $$$setupUI$$$ () {
        root = new JPanel ();
        root.setLayout (new GridLayoutManager (15, 3, new Insets (10, 10, 10, 10), -1, -1));
        root.setEnabled (false);
        final Spacer spacer1 = new Spacer ();
        root.add (spacer1, new GridConstraints (12, 0, 2, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_VERTICAL, 1, GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false));
        cmbFile = new JComboBox ();
        cmbFile.setEditable (true);
        root.add (cmbFile, new GridConstraints (0, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        final JLabel label1 = new JLabel ();
        label1.setText ("SQL Script File");
        root.add (label1, new GridConstraints (0, 0, 1, 1, GridConstraints.ANCHOR_EAST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        btnScriptFile = new JButton ();
        btnScriptFile.setText ("...");
        root.add (btnScriptFile, new GridConstraints (0, 2, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        final JLabel label2 = new JLabel ();
        label2.setText ("Target Database");
        root.add (label2, new GridConstraints (3, 0, 1, 1, GridConstraints.ANCHOR_EAST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        cbDatabase = new JComboBox ();
        cbDatabase.setEditable (true);
        root.add (cbDatabase, new GridConstraints (3, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        final Spacer spacer2 = new Spacer ();
        root.add (spacer2, new GridConstraints (14, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, 1, null, null, null, 0, false));
        final JScrollPane scrollPane1 = new JScrollPane ();
        root.add (scrollPane1, new GridConstraints (12, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false));
        txtLogger = new JTextPane ();
        scrollPane1.setViewportView (txtLogger);
        final JLabel label3 = new JLabel ();
        label3.setText ("Target Package");
        root.add (label3, new GridConstraints (1, 0, 1, 1, GridConstraints.ANCHOR_EAST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        cbPackageName = new JComboBox ();
        cbPackageName.setEditable (true);
        root.add (cbPackageName, new GridConstraints (1, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        final JLabel label4 = new JLabel ();
        label4.setText ("Output Path");
        root.add (label4, new GridConstraints (2, 0, 1, 1, GridConstraints.ANCHOR_EAST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        cbOutPath = new JComboBox ();
        cbOutPath.setEditable (true);
        root.add (cbOutPath, new GridConstraints (2, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        btnOutPath = new JButton ();
        btnOutPath.setText ("...");
        root.add (btnOutPath, new GridConstraints (2, 2, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        final JPanel panel1 = new JPanel ();
        panel1.setLayout (new GridLayoutManager (1, 4, new Insets (0, 0, 0, 0), -1, -1));
        root.add (panel1, new GridConstraints (13, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));
        btnQuit = new JButton ();
        btnQuit.setText ("Quit");
        btnQuit.setMnemonic ('Q');
        btnQuit.setDisplayedMnemonicIndex (0);
        panel1.add (btnQuit, new GridConstraints (0, 3, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        final Spacer spacer3 = new Spacer ();
        panel1.add (spacer3, new GridConstraints (0, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, 1, null, null, null, 0, false));
        btnGenerate = new JButton ();
        btnGenerate.setText ("Generate");
        btnGenerate.setMnemonic ('G');
        btnGenerate.setDisplayedMnemonicIndex (0);
        panel1.add (btnGenerate, new GridConstraints (0, 2, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        btnClear = new JButton ();
        btnClear.setText ("Clear Log");
        panel1.add (btnClear, new GridConstraints (0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        cbPrimitive = new JCheckBox ();
        cbPrimitive.setText ("Generate fields as primitive");
        root.add (cbPrimitive, new GridConstraints (10, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        cbEquals = new JCheckBox ();
        cbEquals.setText ("Generate equals and hashCode method");
        root.add (cbEquals, new GridConstraints (11, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        lblDBServer = new JLabel ();
        lblDBServer.setEnabled (false);
        lblDBServer.setText ("Database Server");
        root.add (lblDBServer, new GridConstraints (4, 0, 1, 1, GridConstraints.ANCHOR_EAST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        txtServer = new JTextField ();
        txtServer.setEnabled (false);
        root.add (txtServer, new GridConstraints (4, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension (150, -1), null, 0, false));
        lblUserName = new JLabel ();
        lblUserName.setEnabled (false);
        lblUserName.setText ("User Name");
        root.add (lblUserName, new GridConstraints (7, 0, 1, 1, GridConstraints.ANCHOR_EAST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        txtUserName = new JTextField ();
        txtUserName.setEnabled (false);
        root.add (txtUserName, new GridConstraints (7, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension (150, -1), null, 0, false));
        lblPassword = new JLabel ();
        lblPassword.setEnabled (false);
        lblPassword.setText ("Password");
        root.add (lblPassword, new GridConstraints (8, 0, 1, 1, GridConstraints.ANCHOR_EAST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        txtPassword = new JPasswordField ();
        txtPassword.setEnabled (false);
        root.add (txtPassword, new GridConstraints (8, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension (150, -1), null, 0, false));
        lblDBName = new JLabel ();
        lblDBName.setEnabled (false);
        lblDBName.setText ("Database Name");
        root.add (lblDBName, new GridConstraints (6, 0, 1, 1, GridConstraints.ANCHOR_EAST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        cbDatabaseName = new JComboBox ();
        cbDatabaseName.setEditable (true);
        cbDatabaseName.setEnabled (false);
        root.add (cbDatabaseName, new GridConstraints (6, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        lblPort = new JLabel ();
        lblPort.setEnabled (false);
        lblPort.setText ("Port");
        root.add (lblPort, new GridConstraints (5, 0, 1, 1, GridConstraints.ANCHOR_EAST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        txtPort = new JTextField ();
        txtPort.setEnabled (false);
        root.add (txtPort, new GridConstraints (5, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension (150, -1), null, 0, false));
        chkJson = new JCheckBox ();
        chkJson.setText ("Generate GSON annotation");
        root.add (chkJson, new GridConstraints (9, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
    }

    /**
     * @noinspection ALL
     */
    public JComponent $$$getRootComponent$$$ () {
        return root;
    }
}